====================== **AWS S3兼容文档** ====================== 前言 ============ 介绍 ^^^^^^^^^^^^^^^^^ 本文档主要是针对NOS兼容AWS S3协议后,与S3的一些异同 支持的SDK =============== - Java SDK - GO SDK - Python SDK - PHP SDK 支持的接口 ============== **Bucket** * List Buckets * Put Bucket * Put Bucket Acl * Get Bucket Acl * Delete Bucket * Get Bucket Location **Object** * Put Object * Put Object Copy * Get Object * Delete Object * Delete Multiple Objects * Head Object * List Objects **Multipart Upload** * Initiate Multipart Upload * Upload Part * List Parts * List Multipart Uploads * Complete Multipart Upload * Abort Multipart Upload 约束说明 ============ ================= ============== 参数 详细说明 ================= ============== 加密 暂不支持 Acl 支持public-read,private Grant 不支持 Lifecycle 不支持 多版本 不支持 Region 支持us-east-1对应的分区为杭州 StorageClass 默认为STANDARD,其他的存储级别暂不支持 Authorization 支持v4,v2,目前只支持在header加入签名信息,暂不支持url签名 MFA授权 不支持 ================= ============== Bucket接口 ==================== List Buckets ^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: AWSCredentials credentials= new BasicAWSCredentials("ak","sk"); AmazonS3 s3Client = new AmazonS3Client(credentials); s3Client.setEndpoint("http://xxx.com");//指定为nos的endpoint List listBuckets = s3Client.listBuckets(); for(Bucket bucket : listBuckets){ System.out.println(bucket.getOwner() + "-->" + bucket.getName() + "-->" + bucket.getCreationDate()); } Put Bucket ^^^^^^^^^^^ 兼容说明 ''''''''' 注意约束说明中的说明即可 代码示例 ''''''''' Java:: s3Client.createBucket("testbucket"); Put Bucket Acl ^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 注意约束说明中的说明即可 代码示例 ''''''''' Java:: s3Client.setBucketAcl(bucketName,CannedAccessControlList.PublicRead); Get Bucket Acl ^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' - 因为NOS的权限不能赋给其他的用户,所以返回的Body中只有一条记录 - 在返回值中,Permission的取值有两种类型,Read(public-read),Write(private) 代码示例 ''''''''' Java:: AccessControlList accessControlList = s3Client.getBucketAcl(bucketName); System.out.println("owner : " + accessControlList.getOwner().getId() + " : " + accessControlList.getOwner().getDisplayName()); for(Grant grant : accessControlList.getGrantsAsList()){//由于不支持授权给其他用户,所以List中只存在一个Grant System.out.println(grant.getGrantee().getIdentifier() + " : " + grant.getPermission() + " : " + grant.getGrantee().getTypeIdentifier()); } Delete Bucket ^^^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: s3Client.deleteBucket(testBucketName); Get Bucket Location ^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: System.out.println(s3Client.getBucketLocation(testBucketName)); Object接口 ==================== Put Object ^^^^^^^^^^^ 兼容说明 ''''''''' - x-amz-tagging 不支持 - x-amz-website-redirect-location 不支持 - Expires 不支持 - Content-Encoding 不支持 - Java SDK不支持http上传 代码示例 ''''''''' Java:: String content = "test put object content"; String testKey = "testKey"; PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName,testKey,"/"); ObjectMetadata objectMetadata = new ObjectMetadata(); objectMetadata.setContentLength(content.length()); Map userMeta = new TreeMap<>(); userMeta.put("userMeta1","meta1"); userMeta.put("userMeta2","meta2"); objectMetadata.setUserMetadata(userMeta); putObjectRequest.setMetadata(objectMetadata); putObjectRequest.setInputStream(new ByteInputStream(content.getBytes(Charset.forName("UTF-8")),content.getBytes(Charset.forName("UTF-8")).length)); s3Client.putObject(putObjectRequest); Put Object Copy ^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' - x-amz-metadata-directive,不支持设置,用户原信息会复制过去 - x-amz-copy-xxx 等参数不支持 - x-amz-storage-class,存储级别使用默认(STANDARD)的就好,NOS不支持其他的存储级别 - x-amz-tagging-directive,x-amz-website-redirect-location 也不支持 代码示例 ''''''''' Java:: CopyObjectResult copyObjectResult = s3Client.copyObject(srcBucket,srcKey,destBucket,destKey); System.out.println(copyObjectResult.getETag()); System.out.println(copyObjectResult.getLastModifiedDate()); Get Object ^^^^^^^^^^^^^ 兼容说明 ''''''''' - NOS不支持的操作,相关的响应头是不会返回的 - If-Unmodified-Since,If-Match,If-None-Match 不支持 代码示例 ''''''''' Java:: String testKey = "testKey"; GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName,testKey); S3Object s3Object = s3Client.getObject(getObjectRequest); System.out.println(s3Object.getObjectMetadata().getContentLength()); if(s3Object != null){ S3ObjectInputStream s3ObjectInputStream = s3Object.getObjectContent(); byte[] buf = new byte[1024]; int readLen; try { while((readLen = s3ObjectInputStream.read(buf)) > -1){ System.out.println(new String(buf,0,readLen)); } System.out.println(s3Object.getObjectMetadata().getUserMetadata()); } catch (IOException e) { e.printStackTrace(); } } Delete Object ^^^^^^^^^^^^^^^ 兼容说明 ''''''''' - NOS会直接删除对象 - 删除对象只需要正确的ak/sk - 返回值中不会包含x-amz-delete-marker和x-amz-version-id 代码示例 ''''''''' Java:: s3Client.deleteObject(bucketName,testKey); Delete Multiple Objects ^^^^^^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: String testBucketName = bucketName; String testKey = "test"; for(int i = 0;i < 5;++i){ s3Client.putObject(testBucketName,testKey + i,"test" + i); } DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(testBucketName); List list = new LinkedList<>(); for(int i = 0; i < 3;++i){ list.add(new DeleteObjectsRequest.KeyVersion(testKey + i)); } deleteObjectsRequest.setKeys(list); deleteObjectsRequest.setQuiet(true); DeleteObjectsResult deleteObjectsResult = s3Client.deleteObjects(deleteObjectsRequest); for(DeleteObjectsResult.DeletedObject deleteObject : deleteObjectsResult.getDeletedObjects()){ System.out.println(deleteObject.getKey()); } Head Object ^^^^^^^^^^^^^ 兼容说明 ''''''''' - 和GetObject一样,也不支持If-Unmodified-Since,If-Match,If-None-Match参数 - 同样的,加密也不支持 - 返回中带有Object该有的信息 代码示例 ''''''''' Java:: ObjectMetadata objectMetadata = s3Client.getObjectMetadata(bucketName,testKey); System.out.println(objectMetadata.getUserMetadata()); System.out.println(objectMetadata.getContentLength()); System.out.println(objectMetadata.getETag()); List Objects ^^^^^^^^^^^^^^^ 兼容说明 ''''''''' S3的List Objects有两个版本(v1,v2),NOS目前只兼容v1 代码示例 ''''''''' Java:: ListObjectsRequest listObjectsRequest = new ListObjectsRequest(); listObjectsRequest.setBucketName("sdktest-private"); List listResult = new ArrayList<>(); ObjectListing listObjects = s3Client.listObjects(listObjectsRequest); do { listResult.addAll(listObjects.getObjectSummaries()); if (listObjects.isTruncated()) { ListObjectsRequest request = new ListObjectsRequest(); request.setBucketName(listObjectsRequest.getBucketName()); request.setMarker(listObjects.getNextMarker()); listObjects = s3Client.listObjects(request); } else { break; } } while (listObjects != null); Multipart Upload接口 ======================== Initiate Multipart Upload ^^^^^^^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' x-amz-website-redirect-location,不支持 代码示例 ''''''''' Java:: InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest( bucketName, key); InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest); Upload Part ^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: UploadPartRequest uploadRequest = new UploadPartRequest() .withBucketName(bucketName).withKey(key) .withUploadId(initResponse.getUploadId()).withPartNumber(i) .withFileOffset(filePosition) .withFile(file) .withPartSize(partSize); s3Client.uploadPart(uploadRequest) List Parts ^^^^^^^^^^^^ 兼容说明 ''''''''' - 对于s3中的encoding头,如果包含了,也不处理 - 因为nos没有生命周期的概念,所以响应中不会加入lifecycle相关的响应头 代码示例 ''''''''' Java:: ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName,key,initResponse.getUploadId()); //initResponse通过上面的init方式获取 s3Client.listParts(listPartsRequest); List Multipart Uploads ^^^^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' - request只支持max-uploads,key-marker参数 - response中,因为不支持有的请求参数,所以有的元素也不会返回,详细见下表: +-----------------------------------+------------+-----------+ |resp header | S3 | NOS | +===================================+============+===========+ | UploadIdMarker | √ | x | +-----------------------------------+------------+-----------+ | NextUploadIdMarker | √ | x | +-----------------------------------+------------+-----------+ | Encoding-Type | √ | x | +-----------------------------------+------------+-----------+ | Initiator | √ | x | +-----------------------------------+------------+-----------+ | CommonPrefixes | √ | x | +-----------------------------------+------------+-----------+ | CommonPrefixes.Prefix | √ | x | +-----------------------------------+------------+-----------+ 代码示例 ''''''''' Java:: ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest(bucketName); listMultipartUploadsRequest.setMaxUploads(2); MultipartUploadListing multipartUploadListing = s3Client.listMultipartUploads(listMultipartUploadsRequest); System.out.println("bucketName : " + multipartUploadListing.getBucketName()); System.out.println(multipartUploadListing.getMaxUploads()); for(MultipartUpload multipartUpload : multipartUploadListing.getMultipartUploads()){ System.out.println(multipartUpload.getInitiated() + " : " + multipartUpload.getUploadId() + " : " + multipartUpload.getStorageClass()); } Complete Multipart Upload ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 除了版本控制和加密,NOS完全兼容S3 代码示例 ''''''''' Java:: CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, key, initResponse.getUploadId(),//为initiateMultipartUpload的返回值 partETags); //partEtags通过上述ListParts构造 Abort Multipart Upload ^^^^^^^^^^^^^^^^^^^^^^^^^ 兼容说明 ''''''''' 接口和S3兼容 代码示例 ''''''''' Java:: s3Client.abortMultipartUpload(new AbortMultipartUploadRequest( bucketName, key, initResponse.getUploadId()));//initResponse为initiateMultipartUpload的返回值 完整的分块上传示例 ^^^^^^^^^^^^^^^^^^ Java:: List partETags = new ArrayList<>(); // Step 1: Initialize. InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest( bucketName, key); InitiateMultipartUploadResult initResponse = s3Client.initiateMultipartUpload(initRequest); File file = new File(filePath); long contentLength = file.length(); long partSize = 5 * 1024 * 1024; // Set part size to 5 MB. try { // Step 2: Upload parts. long filePosition = 0; for (int i = 1; filePosition < contentLength; i++) { // Last part can be less than 5 MB. Adjust part size. partSize = Math.min(partSize, (contentLength - filePosition)); // Create request to upload a part. UploadPartRequest uploadRequest = new UploadPartRequest() .withBucketName(bucketName).withKey(key) .withUploadId(initResponse.getUploadId()).withPartNumber(i) .withFileOffset(filePosition) .withFile(file) .withPartSize(partSize); // Upload part and add response to our list. partETags.add(s3Client.uploadPart(uploadRequest).getPartETag()); ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName,key,initResponse.getUploadId()); s3Client.listParts(listPartsRequest); filePosition += partSize; } // Step 3: Complete. CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, key, initResponse.getUploadId(), partETags); s3Client.completeMultipartUpload(compRequest); } catch (Exception e) { //error,abort s3Client.abortMultipartUpload(new AbortMultipartUploadRequest( bucketName, key, initResponse.getUploadId())); }